
package botlab;

import botlab.engine.physics.RayCast;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

/**
 *
 * @author Quentin
 */
public class UserLib
{
    private static Spatial spatial;
    private static RayCast rc;
    private static float tpf;
    
    private final static float K_LIN=0.03f;
    private final static float K_ANG=0.15f;
    private final static int MAX_LIN_FORCE=150;
    private final static int MAX_ANG_FORCE=10;
    
    private final static float MAX_LIN_VELOCITY=50.f;
    private final static float MAX_ANG_VELOCITY=1.f;
    
    private static int verticalForce=0;
    private static int horizontalForce=0;
    private static int torque=0;

    public static void init(Game app)
    {
        rc = new RayCast(app.getCurrentLevel().getRayCastableNode(),new Node[]{});
        cleanupForces();
    }
    
    public static void cleanupForces()
    {
        verticalForce=0;
        horizontalForce=0;
        torque=0;
    }
    
    public static Spatial getSpatial()
    {
        return spatial;
    }
    
    public static void setSpatial(Spatial s)
    {
        spatial=s;
    }
    
    //Dont't forget to call initUpdate(tpf) before each use of UserLib !
    // i.e at the begining of each update() method.
    public static void initUpdate(float _tpf)
    {
        tpf=_tpf;
        cleanupForces();
    }
    
    /*-----------------------------------------------------------------------//
     * 
     * début des fonction utillisables par l'utilisateur : (sauf ce qui est private)
     */
    
    
    // affiche à l'écran
    /**
     * Print text to screen via Head Up Display
     * @param text the String to display
     */
    public static void print(String text)
    {
        Game.getText().setText(text);
    }
    
    /**
     * Print float value to screen
     * @param value the float value
     */
    public static void print(float value)
    {
        print(Float.toString(value));
    }
    
    /** Retrive the global vector pointing in the direction describe with local angles.
     * (relatively to the vector that point in the direction where the spatial is facing)
     * @param angleV the vertical angle in degree (rotation from x axis)
     * @param angleH the horizontal angle in degree (roation from y axis)
     * @return a {@link Vector3f} corresponding to global(world) direction 
     */
    private static Vector3f directionFromAngles(int angleV,int angleH)
    {
        //vertical --> rotation from x axis
        //horizontal --> rotation from y axis
        
        //angles in radians corresponding to x, y and z axis
        float[] angles = new float[]{(float)Math.toRadians(angleV),(float)Math.toRadians(angleH),0.f};
        
        //the quaternion corresponding to the rotation
        Quaternion rotation = new Quaternion(angles);
        
        //the vector that point in front of the spatial
        Vector3f front = spatial.getLocalRotation().mult(Vector3f.UNIT_Z);
        
        //A vector pointing in the desired direction
        return rotation.mult(front);
    }
    
    // capteur de distance, 2 angles définissent la direction, retourne la dst de la 1ere collision
    /**
     * Retrieve distance from spaceship to the first object detected in the direction
     * specified by angleV and angleH.
     * @param angleV the vertical angle in degree (rotation from x axis)
     * @param angleH the horizontal angle in degree (roation from y axis)
     * @return
     */
    public static float getSensorDst(int angleV,int angleH)
    {
        rc.rayCastFromPoint(spatial.getLocalTranslation(), directionFromAngles(angleV,angleH));
        return rc.getDistance();
    }
    
    private static int verifyAngForce(int f)
    {
        int force=f;
        if (force>MAX_ANG_FORCE)
            force=MAX_ANG_FORCE;
        else if (force<-MAX_ANG_FORCE)
            force=-MAX_ANG_FORCE;
        return force;
    }
    
    private static int verifyLinForce(int f)
    {
        int force=f;
        if (force>MAX_LIN_FORCE)
            force=MAX_LIN_FORCE;
        else if (force<-MAX_LIN_FORCE)
            force=-MAX_LIN_FORCE;
        return force;
    }
    
    /**
     * Apply a torque impulse that make the spaceship turn left
     * @param force the power of the torque impulse 
     */
    public static void turnLeft(int force)
    {
        torque=verifyAngForce(force);
    }
    /**
     * Apply a torque impulse that make the spaceship turn right
     * @param force the torque impulse 
     */
    public static void turnRight(int force)
    {
        turnLeft(-force);
    }
    /**
     * Apply a forse impulse that make the spaceship go forward
     * @param force the force
     */
    public static void goForward(int force)
    {
        int f = verifyLinForce(force); 
        if(f<-0) f=0;
        horizontalForce=f;
    }
    /**
     * Apply a forse impulse that make the spaceship go up
     * @param force the force
     */
    public static void goUp(int force)
    {
        verticalForce=verifyLinForce(force);
    }
    /**
     * Apply a forse impulse that make the spaceship go down
     * @param force the force
     */
    public static void goDown(int force)
    {
        goUp(-force);
    }
    
    private static void applyVerticalForce()
    {
        spatial.getControl(RigidBodyControl.class).applyCentralForce(
                spatial.getLocalRotation().mult(
                    Vector3f.UNIT_Y.mult(verticalForce*tpf))
                );
    }
    private static void applyHorizontalForce()
    {
        spatial.getControl(RigidBodyControl.class).applyCentralForce(
                spatial.getLocalRotation().mult(
                    Vector3f.UNIT_Z.mult(horizontalForce*tpf))
                );
    }
    private static void applyTorque()
    {
        spatial.getControl(RigidBodyControl.class).applyTorque(
                new Vector3f(0,torque*tpf,0)
                );
    }
    private static void applyFriction()
    {   
        Vector3f linV = spatial.getControl(RigidBodyControl.class).getLinearVelocity();
        spatial.getControl(RigidBodyControl.class).applyCentralForce(
                linV.mult(-K_LIN));
        float angV = getAngularVelocity();
        spatial.getControl(RigidBodyControl.class).applyTorque(new Vector3f(0,-K_ANG*angV,0));    
    }
    
    //automatically called (pas pour l'utilisateur)
    public static void applyForces()
    {
        if(getHorizontalVelocity()<MAX_LIN_VELOCITY)
            applyHorizontalForce();
        if(getVerticalVelocity()<MAX_LIN_VELOCITY)
            applyVerticalForce();
        if(getAngularVelocity()<MAX_ANG_VELOCITY)
            applyTorque();

        applyFriction();
    }
    
    /**
     * 
     * @return the horizontal velocity of the spaceship
     */
    public static float getHorizontalVelocity()
    {
        Vector3f velocity = spatial.getControl(RigidBodyControl.class).getLinearVelocity();
        velocity.setY(0);
        return velocity.length();
    }
    
    //Projection de la vitesse sur le vecteur "avant"
    /**
     * 
     * @return the horizontal velocity of the spaceship projected in the front direction
     */
    public static float getHorizontalVelocityA()
    {
        Vector3f velocity = spatial.getControl(RigidBodyControl.class).getLinearVelocity();
        Vector3f front = spatial.getLocalRotation().mult(Vector3f.UNIT_Z);
        return velocity.dot(front);
        
    }
    /**
     *
     * @return the vertical velocity of the spaceship
     */
    public static float getVerticalVelocity()
    {
        Vector3f velocity = spatial.getControl(RigidBodyControl.class).getLinearVelocity();
        return velocity.getY();
    }
    /**
     *
     * @return the angular velocity of the spaceship
     */
    public static float getAngularVelocity()
    {
        return spatial.getControl(RigidBodyControl.class).getAngularVelocity().getY();
    }
    
}
